namespace mecs {

    export class BaseEntity implements IDataNode {

        /** 当前实体在此世界的唯一标识 */
        sid: number;
        /** 当前的实体拥有的组件。目前不支持重名组件 */
        comps: { [compName: string]: BaseComp };
        /** 当前实体所在的世界 */
        world: BaseWorld;
        /** 实体名字，用于调试区分以及获取同名类型的实体列表数据 */
        entityName: string;

        constructor(world: BaseWorld, entityName?: string, sid?: number) {
            this.entityName = entityName;
            this.init(world, sid);
        }

        init(world: BaseWorld, sid?: number) {
            this.comps = {}
            if (!isNaN(sid)) {
                this.sid = sid;
            } else {
                this.sid = world.idCreater.createId()
            }
            this.world = world;
        }

        /** @param joinSysNow 是否马上要加入到某个系统中 */
        addCompByCls<T>(cls: { new(): T }, data?: { [key in keyof T]?: any }, joinSysNow = true): BaseEntity {
            let comp = this.world.compCreater.getComp(cls, data);
            return this.addComp(comp, joinSysNow);
        }

        /** @param joinSysNow 是否马上要加入到某个系统中 */
        addComp(comp: BaseComp, joinSysNow = true): BaseEntity {
            if (this.comps[comp.compName]) {
                throw new Error("不能在一个实体里注册重复名字的组件:" + comp.compName);
            }
            this.comps[comp.compName] = comp;
            if (joinSysNow && this.world && this.world.getEntity(this.sid)) {
                this.world.addEntityToSystem(this);
            }
            return this;
        }
        /** 删除entity中的某个comp，即时清理掉对应的systems中的entity */
        removeComp(compName: string | BaseComp): void {
            let cn = ''
            if (typeof compName != 'string') {
                cn = compName.compName;
            } else {
                cn = compName
            }
            let comp = this.comps[cn];
            if (comp) {
                this.world.systems && this.world.systems.arr.forEach(sys => {
                    if (sys instanceof BaseSysGroup) {
                        let arr = (<BaseSysGroup>sys).subSystems;
                        for (let i = 0; i < arr.length; i++) {
                            let ss = arr[i];
                            if (ss.compTupleNames.indexOf(cn) > -1) {
                                ss.removeEntity(this);
                            }
                        }
                    } else {
                        if (sys.compTupleNames.indexOf(cn) > -1) {
                            sys.removeEntity(this);
                        }
                    }
                })

                this.world.compCreater.cache(comp);
            }
            delete this.comps[cn];
        }
        removeCompByCls<T>(cls: { new(): T, NAME: string }): void {
            this.removeComp(cls.NAME);
        }
        getComp(compName: string) {
            return this.comps[compName];
        }
        getCompByCls<T>(cls: { new(): T, NAME: string }): T {
            return <any>this.comps[cls.NAME];
        }

        hasComp(compName: string): boolean {
            return !!this.getComp(compName);
        }
        hasCompByCls<T>(cls: { new(): T, NAME: string }): boolean {
            return !!this.getComp(cls.NAME);
        }

        cloneData(): ICacheEntity {
            let clone: ICacheEntity = { sid: this.sid, entityName: this.entityName, comps: {} };
            for (let n in this.comps) {
                let c = this.comps[n];
                let compData = c.cloneData();
                if (compData) {
                    clone.comps[n] = compData;
                }
            }
            return clone;
        }

        resetData(datas: ICacheEntity): void {
            for (let n in this.comps) {
                let c = this.comps[n];
                let d = datas.comps[c.compName];
                if (d) {
                    c.resetData(d);
                }
            }
        }

        dispose() {
            if (this.comps) {
                for (let i in this.comps) {
                    this.removeComp(this.comps[i]);
                    if (this.comps[i]) {
                        this.comps[i].dispose()
                    }
                }
            }
            this.comps = null;
            this.world = null;
            this.sid = 0;
        }
    }
}